﻿#if _MSC_VER >= 1600
#pragma execution_character_set("utf-8")
#endif

#include <iostream>
#include <stdio.h>
extern "C" {
#include "SDL.h"
#undef main
}

// set '1' to choose a type of file to play
#define LOAD_BGRA 1
#define LOAD_RGB24 0
#define LOAD_BGR24 0
#define LOAD_YUV420P 0

#define HAS_BORDER 1

// Bit per Pixel
#if LOAD_BGRA
const int bpp = 32;
#elif LOAD_RGB24 | LOAD_BGR24
const int bpp = 24;
#elif LOAD_YUV420P
const int bpp = 12;
#endif

const int screen_width = 500, screen_height = 500;
const int pixel_width = 320, pixel_height = 180;

static unsigned char buffer[pixel_width * pixel_height * bpp / 8];
// BPP=32
#ifndef LOAD_RGB24 | LOAD_BGR24
static char buffer_convert[pixel_width * pixel_height * 4];
#endif
// Convert RGB24/BGR24 to RGB32/BGR32
// And change Endian if needed
void CONVERT_24to32(unsigned char *image_in, unsigned char *image_out, int w,
                    int h) {
    for (int i = 0; i < h; i++)
        for (int j = 0; j < w; j++) {
            // Big Endian or Small Endian?
            //"ARGB" order:high bit -> low bit.
            // ARGB Format Big Endian (low address save high MSB, here is A) in
            // memory : A|R|G|B ARGB Format Little Endian (low address save low
            // MSB, here is B) in memory : B|G|R|A
            if (SDL_BYTEORDER == SDL_LIL_ENDIAN) {
                // Little Endian (x86): R|G|B --> B|G|R|A
                image_out[(i * w + j) * 4 + 0] = image_in[(i * w + j) * 3 + 2];
                image_out[(i * w + j) * 4 + 1] = image_in[(i * w + j) * 3 + 1];
                image_out[(i * w + j) * 4 + 2] = image_in[(i * w + j) * 3];
                image_out[(i * w + j) * 4 + 3] = '0';
            } else {
                // Big Endian: R|G|B --> A|R|G|B
                image_out[(i * w + j) * 4] = '0';
                memcpy(image_out + (i * w + j) * 4 + 1,
                       image_in + (i * w + j) * 3, 3);
            }
        }
}

// Refresh Event
#define REFRESH_EVENT (SDL_USEREVENT + 1)

const int thread_exit = 0;

int refresh_video(void *) {
    while (thread_exit == 0) {
        SDL_Event event;
        event.type = REFRESH_EVENT;
        SDL_PushEvent(&event);
        SDL_Delay(40);
    }
}

int main(void) {
    if (SDL_Init(SDL_INIT_VIDEO)) {
        printf("Could not initialize SDL - %s\n", SDL_GetError());
        return -1;
    }

    SDL_Window *screen;
    // SDL 2.0 Support for multiple windows
    screen = SDL_CreateWindow("使用SDL2播放视频", SDL_WINDOWPOS_UNDEFINED,
                              SDL_WINDOWPOS_UNDEFINED, screen_width,
                              screen_height, SDL_WINDOW_OPENGL);
    if (!screen) {
        printf("SDL: could not create window - exiting:%s\n", SDL_GetError());
        return -1;
    }
    SDL_Renderer *sdlRenderer = SDL_CreateRenderer(screen, -1, 0);

    Uint32 pixformat = 0;
#if LOAD_BGRA
    // Note: ARGB8888 in "Little Endian" system stores as B|G|R|A
    pixformat = SDL_PIXELFORMAT_ARGB8888;
#elif LOAD_RGB24
    pixformat = SDL_PIXELFORMAT_RGB888;
#elif LOAD_BGR24
    pixformat = SDL_PIXELFORMAT_BGR888;
#elif LOAD_YUV420P
    // IYUV: Y + U + V  (3 planes)
    // YV12: Y + V + U  (3 planes)
    pixformat = SDL_PIXELFORMAT_IYUV;
#endif

    SDL_Texture *sdlTexture =
        SDL_CreateTexture(sdlRenderer, pixformat, SDL_TEXTUREACCESS_STREAMING,
                          pixel_width, pixel_height);

    int border = 0;
    // Make picture smaller, Add a "border"
#if HAS_BORDER
    border = 30;
#endif
    SDL_Rect sdlRect;
    sdlRect.x = 0 + border;
    sdlRect.y = 0 + border;
    sdlRect.w = screen_width - border * 2;

    sdlRect.h = screen_height - border * 2;

    FILE *fp = nullptr;
#if LOAD_BGRA
    fopen_s(&fp, "E://workspace//FFMEPG//SDL//PlayVideo//test_bgra_320x180.rgb",
            "rb+");
#elif LOAD_RGB24
    fp = fopen("../test_rgb24_320x180.rgb", "rb+");
#elif LOAD_BGR24
    fp = fopen("../test_bgr24_320x180.rgb", "rb+");
#elif LOAD_YUV420P
    fp = fopen("../test_yuv420p_320x180.yuv", "rb+");
#endif
    if (fp == nullptr) {
        printf("cannot open this file\n");
        return -1;
    }

    SDL_Thread *refresh_thread = SDL_CreateThread(refresh_video, NULL, NULL);
    if (refresh_thread == nullptr) {
        printf("Create Thread failed\n");
        return -1;
    }
    SDL_Event event;
    while (1) {
        // Wait
        SDL_WaitEvent(&event);
        if (event.type == REFRESH_EVENT) {
            if (fread_s(buffer, sizeof(buffer), sizeof(unsigned char),
                        pixel_width * pixel_height * bpp / 8,
                        fp) != pixel_width * pixel_height * bpp / 8) {
                // Loop
                fseek(fp, 0, SEEK_SET);
                fread_s(buffer, sizeof(buffer), sizeof(unsigned char),
                        pixel_width * pixel_height * bpp / 8, fp);
            }

#if LOAD_BGRA
            // We don't need to change Endian
            // Because input BGRA pixel data(B|G|R|A) is same as ARGB8888 in
            // Little Endian (B|G|R|A)
            SDL_UpdateTexture(sdlTexture, nullptr, buffer,
                              pixel_width * 4); //设置纹理的数据
#elif LOAD_RGB24 | LOAD_BGR24
            // change 24bit to 32 bit
            // and in Windows we need to change Endian
            CONVERT_24to32(buffer, buffer_convert, pixel_width, pixel_height);
            SDL_UpdateTexture(sdlTexture, NULL, buffer_convert,
                              pixel_width * 4);
#elif LOAD_YUV420P
            SDL_UpdateTexture(sdlTexture, NULL, buffer, pixel_width);
#endif

            SDL_RenderClear(
                sdlRenderer); // Use this function to clear the current
                              // rendering target with the drawing color.
            SDL_RenderCopy(sdlRenderer, sdlTexture, nullptr,
                           &sdlRect);       //纹理赋值给渲染器
            SDL_RenderPresent(sdlRenderer); //纹理显示
            // Delay 40ms
            SDL_Delay(40);

        } else if (event.type == SDL_QUIT) {
            break;
        }
    }

    return 0;
}
